package cn.elead.chaos.framework.web.enums;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils;

import cn.elead.chaos.core.util.ApplicationUtils;
import cn.elead.chaos.framework.properties.ChaosProperties;
import cn.elead.chaos.framework.util.EnumUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class EnumRegistrar implements ImportBeanDefinitionRegistrar {

	private static final String RESOURCE_PATTERN = "**/*.class";
	private static final String CLASSPATH_URL_PREFIX = "classpath:";

	@Override
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

		AnnotationAttributes annAttr = AnnotationAttributes
				.fromMap(importingClassMetadata.getAnnotationAttributes(EnumScan.class.getName()));
		String[] basePackages = annAttr.getStringArray("value");
		if (ObjectUtil.isEmpty(basePackages)) {
			basePackages = annAttr.getStringArray("basePackages");
		}
		if (ObjectUtil.isEmpty(basePackages)) {
			basePackages = new String[] { ClassUtils.getPackageName(importingClassMetadata.getClassName()) };
		}
		// 配置文件中配置扫描路径TODO
		// basePackages = configScan(basePackages);
		List<IEnum> candidates = scanPackages(basePackages, null, null);
		if (candidates.isEmpty()) {
			return;
		}
		for (IEnum clazz : candidates) {
			EnumFactory.add(clazz);
		}
	}

	/**
	 * 在配置文件中添加了扫描路径
	 * 
	 * @param basePackages
	 * @return
	 */
	@SuppressWarnings("unused")
	private String[] configScan(String[] basePackages) {
		if (ObjectUtil.isNotEmpty(ApplicationUtils.getContext())) {
			// 在配置文件中添加了扫描路径
			ChaosProperties chaos = ApplicationUtils.getBean(ChaosProperties.class);
			if (ObjectUtil.isEmpty(basePackages)) {
				if (ObjectUtil.isNotEmpty(chaos)) {
					cn.elead.chaos.framework.properties.EnumScan enumScan = chaos.getEnumScan();
					if (ObjectUtil.isNotEmpty(enumScan)) {
						if (ObjectUtil.isNotEmpty(enumScan.getBasePackages())) {
							basePackages = StrUtil.split(enumScan.getBasePackages(), StrUtil.COMMA);
						}
					}
				}
			} else if (ObjectUtil.isNotEmpty(chaos)) {
				cn.elead.chaos.framework.properties.EnumScan enumScan = chaos.getEnumScan();
				if (ObjectUtil.isNotEmpty(enumScan)) {
					if (ObjectUtil.isNotEmpty(enumScan.getBasePackages())) {
						String[] paths = StrUtil.split(enumScan.getBasePackages(), StrUtil.COMMA);
						for (String path : paths) {
							ArrayUtil.append(basePackages, path);
						}
					}
				}
			}
		}
		return basePackages;
	}

	@SuppressWarnings("rawtypes")
	private List<IEnum> scanPackages(String[] basePackages, List<TypeFilter> includeFilters,
			List<TypeFilter> excludeFilters) {
		List<IEnum> candidates = new ArrayList<IEnum>();
		for (String pkg : basePackages) {
			try {
				List<IEnum> list = findCandidateClasses(pkg, includeFilters, excludeFilters);
				if (list != null) {
					candidates.addAll(list);
				}
			} catch (IOException e) {
				log.error("扫描包[" + pkg + "]时出现异常", e);
				continue;
			}
		}
		return candidates;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private List<IEnum> findCandidateClasses(String basePackage, List<TypeFilter> includeFilters,
			List<TypeFilter> excludeFilters) throws IOException {
		List<IEnum> candidates = new ArrayList<IEnum>();
		String packageSearchPath = CLASSPATH_URL_PREFIX + replaceDotByDelimiter(basePackage) + '/' + RESOURCE_PATTERN;
		ResourceLoader resourceLoader = new DefaultResourceLoader();
		MetadataReaderFactory readerFactory = new SimpleMetadataReaderFactory(resourceLoader);
		Resource[] resources = ResourcePatternUtils.getResourcePatternResolver(resourceLoader)
				.getResources(packageSearchPath);
		for (Resource resource : resources) {
			MetadataReader reader = readerFactory.getMetadataReader(resource);
			Class<?> candidateClass = transform(reader.getClassMetadata().getClassName());
			if (candidateClass != null && IEnum.class.isAssignableFrom(candidateClass)
					&& candidateClass != IEnum.class) {
				candidates.addAll(EnumUtil.of(candidateClass));
				log.info("扫描到Enum类：{}", candidateClass.getSimpleName());
			}
		}
		return candidates;
	}

	private Class<?> transform(String className) {
		Class<?> clazz = null;
		try {
			clazz = ClassUtils.forName(className, this.getClass().getClassLoader());
		} catch (ClassNotFoundException e) {
			log.info("未找到类:{}", className);
		}
		return clazz;
	}

	protected boolean isCandidateResource(MetadataReader reader, MetadataReaderFactory readerFactory,
			List<TypeFilter> includeFilters, List<TypeFilter> excludeFilters) throws IOException {
		if (excludeFilters != null && excludeFilters.size() > 0) {
			for (TypeFilter tf : excludeFilters) {
				if (tf.match(reader, readerFactory)) {
					return false;
				}
			}
		}
		if (includeFilters != null && includeFilters.size() > 0) {
			for (TypeFilter tf : includeFilters) {
				if (tf.match(reader, readerFactory)) {
					return true;
				}
			}
		}
		return false;
	}

	private String replaceDotByDelimiter(String path) {
		return StrUtil.replace(path, ".", "/");
	}
}
